gRPC 통신 및 BFF 아키텍처
gRPC란 무엇인가?
gRPC는 서로 다른 컴퓨터에 있는 프로그램들이 대화할 수 있게 해주는 도구. 한 프로그램이 다른 프로그램의 기능을 호출할 수 있게 해줌.
gRPC 사용 이유
- 빠름: 일반적인 웹 통신(JSON)보다 훨씬 빠름
- 안전함: 데이터 형식을 미리 정해놓아서 실수 방지
- 편리함: 여러 프로그래밍 언어에서 같은 방식으로 사용 가능
BFF(Backend For Frontend)란?
BFF는 프론트엔드(사용자가 보는 화면)를 위한 전용 백엔드 서버. '데이터 조립공장'
필요한 이유
- 모바일 앱: 작은 화면이라 최소한의 데이터만 필요
- 웹 사이트: 큰 화면이라 더 많은 정보를 보여줄 수 있음
- 각각에 맞는 서버를 따로 만들어서 최적화된 서비스 제공
gRPC + BFF 조합
사용자 앱 → BFF 서버 → 여러 마이크로서비스들 (사용자 정보, 상품 정보, 주문 정보 등)
장점
- 속도: 서버 간 통신이 매우 빠름
- 안정성: 정해진 규칙대로만 데이터를 주고받음
- 유지보수: 각 부분을 독립적으로 관리 가능
간단한 예시
상황: 쇼핑몰 앱에서 사용자 정보를 가져오는 경우
기존 방식 (REST API)
GET /api/user/123
→ 응답: JSON 형태의 사용자 정보
gRPC 방식
GetUser(사용자ID: 123)
→ 응답: 미리 정의된 구조의 사용자 정보
차이점
- gRPC는 미리 정해진 형식으로만 데이터를 주고받음
- 더 빠르고 안전함(타입 안전성, 스키마 검증, 버전 호환성 : 필드명 강제 + 타입 강제)
- 설정이 조금 더 복잡함
gRPC 동작 과정
1. Proto 파일 작성 (서버와 클라이언트 공통 계약서)
syntax = "proto3";
// 사용자 서비스 정의 (어떤 기능을 제공할지)
service UserService {
rpc GetUser(UserRequest) returns (UserResponse);
}
// 요청할 때 보낼 데이터
message UserRequest {
string user_id = 1; // 1번: 사용자 ID
}
// 응답할 때 받을 데이터
message UserResponse {
string user_id = 1; // 1번: 사용자 ID
string name = 2; // 2번: 이름
string email = 3; // 3번: 이메일
bool success = 4; // 4번: 성공 여부
}
2. 전체 통신 흐름
클라이언트 서버
| |
| 1. gRPC 요청 (바이너리) |
| 사용자ID: "123" |
|------------------------------>|
| | 2. 바이너리 → UserRequest 객체
| | 3. 비즈니스 로직 처리
| | - 데이터베이스 조회
| | - 실제 사용자 데이터 찾기
| | 4. UserResponse 객체 생성
| | - proto 규칙에 맞게 데이터 포장
| | 5. UserResponse → 바이너리
| 6. gRPC 응답 (바이너리) |
| 이름: "홍길동" |
| 이메일: "hong@example.com" |
|<------------------------------|
7. 바이너리 → UserResponse 객체 |
3. 핵심 포인트
- Proto 파일: 서버와 클라이언트가 똑같이 사용하는 공통 규칙서
- 서버 역할: proto 규칙에 맞춰 실제 데이터를 만들어서 gRPC 형식으로 전송
- 클라이언트 역할: proto 규칙에 맞춰 요청하고 응답 받기
- 자동 변환: 바이너리 ↔ 객체 변환은 gRPC가 자동 처리
송수신 과정
1. 송신측 (클라이언트)
UserRequest.newBuilder()
.setUserId("123") // user_id 필드 → 필드번호 1로 변환
.setName("홍길동") // name 필드 → 필드번호 2로 변환
.setAge(25) // age 필드 → 필드번호 3으로 변환
.build();
↓ 자동 변환
바이너리 예시 : 0x0A 0x03 0x31 0x32 0x33 0x12 0x09 0xED 0x99 0x8D 0xEA 0xB8 0xB8 0xEB 0x8F 0x99 0x1A 0x11
2. 수신측 (서버)
바이너리 예시 : 0x68 0x6F 0x6E 0x67 0x40 0x65 0x78 0x61 0x6D 0x70 0x6C 0x65 0x2E 0x63 0x6F 0x6D 0x20 0x01
↓ 자동 해석
필드1 = "123" → getUserId() 메서드로 접근 가능
필드2 = "홍길동" → getName() 메서드로 접근 가능
필드3 = 25 → getAge() 메서드로 접근 가능
크기 비교
JSON 방식
json
{"user_id": "123", "name": "홍길동", "age": 25}
→ 52바이트 (필드명까지 다 포함)
gRPC 바이너리
[1]["123"][2]["홍길동"][3][25]
→ 약 15바이트 (필드명 없이 번호만)
심화: gRPC 통신 및 BFF 아키텍처
gRPC 개요 gRPC는 Google에서 개발한 고성능 오픈소스 RPC(Remote Procedure Call) 프레임워크.
- Protocol Buffers(protobuf): 언어 중립적인 효율적인 직렬화 도구로 데이터 구조 정의
- HTTP/2 기반: 연결 멀티플렉싱, 헤더 압축, 양방향 스트리밍 제공
- 다국어 지원: Java, Go, C++, Python, Node.js 등 다양한 언어 지원
- 높은 성능: JSON/XML보다 빠른 바이너리 직렬화, 적은 네트워크 사용량
- 강력한 타입 체크: 컴파일 타임에 타입 검증으로 런타임 오류 감소
BFF(Backend For Frontend) 패턴 BFF는 특정 프론트엔드 애플리케이션 유형에 최적화된 백엔드 서비스를 구축하는 아키텍처 패턴.
- 목적별 API 집계: 각 클라이언트 유형(웹, 모바일, 데스크톱)에 최적화된 API 제공
- 관심사 분리: 프론트엔드 요구사항에 맞게 백엔드 로직 분리
- 응답 최적화: 단일 요청으로 여러 마이크로서비스의 데이터 조합 가능
- 클라이언트 특화 캐싱: 특정 프론트엔드에 필요한 데이터만 캐싱
통신 흐름
프론트엔드 클라이언트 (HTTP/JSON) → BFF 계층 (HTTP → gRPC 변환) → 마이크로서비스 (gRPC 처리)
주요 장점
- 성능 최적화: 효율적인 바이너리 직렬화로 네트워크 부하 감소, 지연 시간 감소 및 처리량 증가
- 개발 효율성: 서비스 인터페이스를 .proto 파일로 명확하게 정의, 코드 생성으로 클라이언트/서버 구현 간소화
- 확장성: 서비스 메서드 추가가 용이하며, 기존 인터페이스 유지하며 새 기능 추가 가능
- 다양한 통신 패턴: Unary, Server Streaming, Client Streaming, Bidirectional Streaming RPC 지원
최근 동향: JSON-RPC의 부상과 성능 비교
JSON-RPC란? JSON-RPC는 JSON을 사용하는 원격 프로시저 호출 프로토콜. gRPC와 비슷한 목적이지만 더 간단한 접근 방식 제공.
주목받는 이유
Google의 A2A (Agent-to-Agent) 채택
- Google이 AI 에이전트 간 통신을 위해 JSON-RPC 기반 프로토콜 채택
- 복잡한 AI 시스템에서 에이전트들이 서로 소통할 때 사용
MCP (Model Context Protocol)
- Anthropic에서 개발한 AI 모델과 도구 간의 표준 통신 프로토콜
- JSON-RPC 2.0을 기반으로 구축
- AI 모델이 외부 도구나 데이터에 접근할 때 사용
성능 비교: JSON-RPC vs gRPC
속도 측면에서는 gRPC가 압도적으로 빠름!
데이터 크기 비교
JSON-RPC (텍스트 형태)
{
"jsonrpc": "2.0",
"result": {
"userId": "user_12345",
"name": "홍길동",
"email": "hong@example.com",
"status": "ACTIVE"
},
"id": 1
}
→ 약 150-200 바이트
gRPC (바이너리 형태)
바이너리 데이터로 압축된 형태 예시 : 0x0A 0x03 0x31 0x32 0x33 0x12 0x09 0xED 0x99 0x8D 0xEA 0xB8 0xB8 0xEB 0x8F 0x99 0x1A 0x11 0x68 0x6F 0x6E 0x67 0x40 0x65 0x78 0x61 0x6D 0x70 0x6C 0x65 0x2E 0x63 0x6F 0x6D 0x20 0x01
→ 약 50-80 바이트
처리 속도 비교
| 구분 | JSON-RPC | gRPC | 차이 |
|---|---|---|---|
| 직렬화 속도 | 느림 (텍스트 파싱) | 빠름 (바이너리) | gRPC 3-5배 빠름 |
| 네트워크 전송 | 느림 (큰 데이터) | 빠름 (작은 데이터) | gRPC 2-3배 빠름 |
| 역직렬화 속도 | 느림 (JSON 파싱) | 빠름 (바이너리) | gRPC 3-5배 빠름 |
JSON-RPC가 주목받는 이유
성능이 전부가 아니기 때문!
JSON-RPC vs gRPC 비교
| 특징 | JSON-RPC | gRPC |
|---|---|---|
| 성능 | 보통 | 매우 높음 |
| 개발 복잡성 | 매우 간단 | 상대적으로 복잡 |
| 디버깅 | 쉬움 (JSON은 읽기 쉬움) | 어려움 (바이너리 형태) |
| AI 통합 | 매우 좋음 | 보통 |
| 설정 | 최소한의 설정 | 복잡한 설정 필요 |
실제 사용 시나리오별 선택
고성능이 중요한 경우 → gRPC
마이크로서비스 간 초당 10,000건 처리
→ gRPC의 성능 우위가 명확히 드러남
개발 편의성이 중요한 경우 → JSON-RPC
AI 에이전트가 웹 API 호출
→ JSON-RPC가 훨씬 간단하고 실용적
언제 무엇을 사용할까?
gRPC를 사용하면 좋은 경우
- 마이크로서비스 간 고성능 통신이 필요한 경우
- 대용량 데이터 처리가 필요한 경우
- 엄격한 타입 체크가 중요한 경우
- 스트리밍이 필요한 경우
JSON-RPC를 사용하면 좋은 경우
- AI 에이전트나 봇 간의 통신
- 빠른 프로토타이핑이 필요한 경우
- 단순한 API가 필요한 경우
- 디버깅과 테스트가 중요한 경우
BFF를 사용하면 좋은 경우
- 모바일 앱과 웹 사이트를 둘 다 제공하는 서비스
- 각 클라이언트마다 다른 데이터가 필요한 경우
- 여러 마이크로서비스의 데이터를 조합해야 하는 경우
결론
성능만 보면 gRPC가 압도적으로 빠르지만, 현실에서는 상황에 따라 선택해야 함:
- 마이크로서비스: 성능이 중요 → gRPC 선택
- AI 통합: 개발 편의성이 중요 → JSON-RPC 선택
- 프로토타입: 빠른 개발이 중요 → JSON-RPC 선택
Google이 A2A에서 JSON-RPC를 선택한 이유도 성능보다는 AI 에이전트들이 쉽게 이해하고 사용할 수 있는 단순함과 호환성 때문.
심화
성능 순서
일반적인 속도 순서:
gRPC > JSON-RPC ≈ REST API
JSON-RPC와 REST API는 거의 비슷한 성능
왜 비슷한 성능일까?
공통점들:
- 둘 다 JSON 텍스트 사용
- 둘 다 HTTP 기반 (보통 HTTP/1.1)
- 둘 다 텍스트 파싱 필요
JSON-RPC:
{
"jsonrpc": "2.0",
"method": "getUser",
"params": {"userId": "123"},
"id": 1
}
REST API:
POST /api/users/123
{
"action": "get"
}
세부 차이점
JSON-RPC의 장점:
- 약간 더 간결한 구조 (메서드명 + 파라미터만)
- 배치 요청 가능 (여러 요청을 하나로 묶기)
- HTTP 헤더 오버헤드가 조금 적음
REST API의 단점:
- URL 파싱 추가 필요 (
/api/users/123/orders/456) - HTTP 메서드 처리 오버헤드 (GET, POST, PUT, DELETE)
- 더 많은 HTTP 헤더
실제 성능 비교
데이터 크기 (같은 응답 기준):
JSON-RPC:
{
"jsonrpc": "2.0",
"result": {"name": "홍길동", "email": "hong@example.com"},
"id": 1
}
→ 약 80-90바이트
REST API:
{
"status": "success",
"data": {"name": "홍길동", "email": "hong@example.com"}
}
→ 약 75-85바이트
gRPC:
[바이너리 형태]
→ 약 25-35바이트
처리 속도 비교
| 구분 | REST API | JSON-RPC | gRPC |
|---|---|---|---|
| 직렬화 | JSON 파싱 | JSON 파싱 | 바이너리 |
| 네트워크 | HTTP/1.1 | HTTP/1.1 | HTTP/2 |
| 오버헤드 | URL + 메서드 | 메서드명만 | 필드번호 |
| 상대속도 | 1x | 1.1x | 3-5x |
실제 상황에서는?
JSON-RPC가 REST보다 약간 빠른 경우:
- 배치 요청 사용시
- 복잡한 URL 구조가 없어서
- HTTP 헤더가 더 간결해서
하지만 차이는 미미함:
- 둘 다 JSON 파싱이 병목
- 네트워크 지연이 더 큰 요소
- 실제로는 거의 체감 안됨
결론
성능 순서:
- gRPC: 압도적으로 빠름 (3-5배)
- JSON-RPC: REST보다 약간 빠름 (10-20% 정도)
- REST API: 기준점
JSON-RPC를 선택하는 이유는 성능보다는:
- 단순함 (REST보다 구조가 간단)
- AI 호환성 (AI가 이해하기 쉬움)
- 배치 처리 (여러 요청을 한번에)